Google Cloud で Packer を使って カスタムイメージをビルドしてみる

Google Cloud で Packer を使って カスタムイメージをビルドしてみる

Packer というサーバテンプレーティングツールを利用して Google Cloud 上に カスタムイメージをビルドする検証を行いました。Packer の実行方法や Google Cloud の連携する際のアーキテクチャ、ビルドプロセスについても解説します。
Clock Icon2024.08.30

Packer とは

Packer は Terraform で馴染みのある Hashicorp社が提供するサーバテンプレーティングツールです。
Terraform などのプロビジョニングツールは VMインスタンスやデータベースといったインフラリソースをコードによってデプロイできるものですが、Packer は OS、ソフトウェア、ライブラリ、設定ファイル等を含んだイメージをコードで管理できるのが特徴のツールです。

Terraform でもソースイメージを指定して起動スクリプトを実行することにより特定のソフトウェアのインストールや設定ファイルの編集を行うことはできますが以下のような課題が考えられます。

  • 複数行にわたる起動スクリプト記述により Terraform コードによる管理が煩雑になる
  • デプロイ時にスクリプト実行の余計な時間がかかる
  • スクリプト失敗時のトレースやエラーハンドリング、再試行が困難

Packer で事前に必要なソフトウェアや設定がインストールされた構成済カスタムイメージを作成し、それを Terraform で参照してデプロイすることで、コード管理の簡素化やデプロイの効率化が実現できます。

スクリーンショット 2024-08-30 102058
Packer x Terraform

Packer でカスタムイメージを作成するには Packer テンプレート と呼ばれる設定ファイルを利用しますが、HCL2(Hashicorp Configuration Language 2) で記述できるので、 Terraform に馴染みがある方でしたら習得も容易ではないでしょうか。

Packer と Google Cloud

Packer で利用可能な googlecompute プラグイン(Packer ビルダー) が Hashicorp より提供されています。これにより、Google Cloud 上でデプロイ可能なカスタムイメージを作成することができます。

https://developer.hashicorp.com/packer/integrations/hashicorp/googlecompute/latest/components/builder/googlecompute

また、Google Cloud の Cloud Shell には Packer がプリインストールされていました。今回の検証では Cloud Shell を Packer の実行環境として利用してみます。

$ packer --version
1.9.4

さらに、Google Cloud 上でビルドプロセスを容易に実現できる Cloud Build では、Packer コミュニティビルダーイメージを利用して packer コマンドを呼び出すこともできます。
これにより、Packer によるカスタムイメージ作成と Terraform によるインスタンスのデプロイを Cloud Build の一連のパイプラインで容易に実現することも可能です。

https://cloud.google.com/build/docs/building/build-vm-images-with-packer?hl=ja

アーキテクチャ概要

検証の前に、Google Cloud での Packer を使った カスタムイメージビルドのアーキテクチャ概要を説明します。

architecture
Packer による Google Cloud でのカスタムイメージビルドアーキテクチャ概要

まずは Packer テンプレートを用意します。Packer テンプレートには、インストールする Packer ビルダー(カスタムイメージを作成するコンポーネント) や カスタムイメージの設定が記述されています。事前準備として、packer initで Packerテンプレートを参照して Google Cloud 用の Packer ビルダーをインストールします。その後、packer buildにより Google Cloud 上で VMインスタンスの起動と設定、カスタムイメージの作成を行います。

Packer テンプレート

Packer テンプレートは、ビルドするイメージやそのビルド方法などを定義する設定ファイルです。今回は以下の Packer テンプレートを用意します。

googlecloud.pkr.hcl
packer {
  required_plugins {
    googlecompute = {
      version = ">= 1.1.6"
      source  = "github.com/hashicorp/googlecompute"
    }
  }
}

source "googlecompute" "default" {
  project_id  = "<Project Name>"
  zone        = "asia-northeast1-a"
  machine_type = "e2-micro"
  image_name  = "packer-test-image"
  disk_size   = 10
  source_image_family = "debian-12"
  ssh_username = "packer"
}

build {
  sources = ["source.googlecompute.default"]

  provisioner "shell" {
    inline = [
      # NGINX のインストール
      "sudo apt-get update",
      "sudo apt-get install -y nginx",

      # NGINX のサービスを開始
      "sudo systemctl enable nginx"
    ]
  }

}

Packer テンプレートは HCL2(Hashicorp Configuration Language 2) で記述できます。以下より各ブロックについて説明します。

packer ブロック

Packerバージョンの指定や Packer ビルダー(プラグイン)の指定など、Packerの設定を定義します。

required_plugins: イメージのビルドに必要とするPacker ビルダーのプラグインを指定します。ここでは github.com/hashicorp/googlecompute を指定しています。

https://github.com/hashicorp/packer-plugin-googlecompute/tree/main

source ブロック

仮想化の種類、インスタンスの起動方法、及びイメージへの接続方法などを定義します。後述する Build ブロックによって呼び出されます。

source ブロックの Required 項目について説明します。

  • project_id: インスタンスの起動先及びカスタムイメージ保存先のプロジェクトID。
  • source_imageまたはsource_image_family: ソースイメージまたはソースイメージファミリーを指定。ソースイメージファミリーを指定した場合は、非推奨ではない最新のイメージが選択される。※1
  • zone: Packer によるカスタムイメージを作成するためにインスタンスを起動するゾーン。
  • ssh_username: 起動したインスタンスに SSH ログインする際のユーザ。SSH にて接続する場合は必須。packer ビルダー は本設定に従って sudo アクセス権を持つユーザを作成。※2

※1: source_imageまたはsource_image_familyに指定可能なリストはgcloud compute image listで確認できます。--filterを利用して特定のディストリビューションに絞りこむことも可能です。

$ gcloud compute images list --filter="name~'debian'"
NAME: debian-11-bullseye-arm64-v20240815
PROJECT: debian-cloud
FAMILY: debian-11-arm64
DEPRECATED: 
STATUS: READY

NAME: debian-11-bullseye-v20240815
PROJECT: debian-cloud
FAMILY: debian-11
DEPRECATED: 
STATUS: READY

NAME: debian-12-bookworm-arm64-v20240815
PROJECT: debian-cloud
FAMILY: debian-12-arm64
DEPRECATED: 
STATUS: READY

NAME: debian-12-bookworm-v20240815
PROJECT: debian-cloud
FAMILY: debian-12
DEPRECATED: 
STATUS: READY

※2: デフォルトのログイン方法は SSH となります。ssh_username のみを指定した場合、Packer ビルダーは SSH キーペアを作成して公開鍵を Compute Engine のインスタンスメタデータに保存します。Compute Engine のインスタンス起動時にメタデータを参照して公開鍵をインスタンスに保存します。なお、use_os_login を利用して OS Login による SSH ログインとすることも可能です。

その他 optional の設定やデフォルト値については以下をご参照ください。
https://developer.hashicorp.com/packer/integrations/hashicorp/googlecompute/latest/components/builder/googlecompute#configuration-reference

build ブロック

イメージ作成のためのソースの指定やプロビジョニング方法をなど、Packer が実行するビルドプロセスの具体的な手順を定義します。

sources: イメージ作成のためのソースを指定。ここでは source ブロックで定義したgooglecompute.defaultを指定。
provisioner: パッケージのインストールやアプリケーションのダウンロード、ユーザの作成など、イメージのプロビジョニングに使用するスクリプトやツールを指定。ここで指定したshellはシェルスクリプトを実行するためのプロビジョナー。

本検証では NGINX をインストールし、インスタンス起動時にサービス開始するスクリプトをプロビジョナーで実行するビルドプロセスを定義しています。

その他のプロビジョナーは以下をご参照ください。
https://developer.hashicorp.com/packer/docs/provisioners

やってみた

Packer テンプレートの準備

Google Cloud コンソールより Cloud Shell を起動し、先ほどの Packer テンプレート(googlecloud.pkr.hcl)を検証用ディレクトリに配置します。

$ mkdir packer_test
$ cd packer_test
$ ls
googlecloud.pkr.hcl

packer init

packer init <Packer Template> で Packer ビルダーのプラグイン googlecomputeをインストールします。

$ packer init googlecloud.pkr.hcl 
Installed plugin github.com/hashicorp/googlecompute v1.1.6 in "/home/<Project Name>/.config/packer/plugins/github.com/hashicorp/googlecompute/packer-plugin-googlecompute_v1.1.6_x5.0_linux_amd64"

packer build

packer build <Packer Template> でビルドを実行します。

$ packer build googlecloud.pkr.hcl 
googlecompute.default: output will be in this color.

==> googlecompute.default: Checking image does not exist...
# SSHキーペアの作成
==> googlecompute.default: Creating temporary RSA SSH key for instance...
==> googlecompute.default: no persistent disk to create
# ソースイメージの参照
==> googlecompute.default: Using image: debian-12-bookworm-v20240815
# インスタンスの作成
==> googlecompute.default: Creating instance...
    googlecompute.default: Loading zone: asia-northeast1-a
    googlecompute.default: Loading machine type: e2-micro
    googlecompute.default: Requesting instance creation...
    googlecompute.default: Waiting for creation operation to complete...
    googlecompute.default: Instance has been created!
# インスタンスの起動
==> googlecompute.default: Waiting for the instance to become running...
# 外部IPアドレスの作成
googlecompute.default: IP: 34.84.159.79
# 外部IPアドレスを宛先として SSH 接続
==> googlecompute.default: Using SSH communicator to connect: 34.84.159.79
==> googlecompute.default: Waiting for SSH to become available...
==> googlecompute.default: Connected to SSH!
# プロビジョナーを利用してシェルスクリプトを実行
==> googlecompute.default: Provisioning with shell script: /tmp/packer-shell4250237553
...
# インスタンスの削除
==> googlecompute.default: Deleting instance...
    googlecompute.default: Instance has been deleted!
# イメージの作成
==> googlecompute.default: Creating image...
==> googlecompute.default: Deleting disk...
    googlecompute.default: Disk has been deleted!
Build 'googlecompute.default' finished after 2 minutes 31 seconds.

==> Wait completed after 2 minutes 31 seconds

==> Builds finished. The artifacts of successful builds are:
--> googlecompute.default: A disk image was created in the '<Project Name>' project: packer-test-image

上記のビルドプロセスについて補足します。

Packer ビルダーは SSH キーペアを作成し、公開鍵をインスタンスメタデータに保存します。Packer テンプレートに設定したソースイメージやマシンタイプなどの設定、インスタンスメタデータの公開鍵などを参照してインスタンスを作成・起動します。インスタンスにアタッチする外部IPアドレスを作成し、Packer ビルダーは外部IPアドレスを宛先として SSH 接続します。SSH によるログイン後、プロビジョナーを利用してシェルスクリプトを実行しプロビジョニングを行います。最後にインスタンスの削除とカスタムイメージの作成を行いビルドプロセスが完了します。

build process
Packer によるビルドプロセス

ビルドプロセス中に Packer によって作成されたインスタンスが Google Cloud コンソールの Compute Engine のページからも確認できます。ビルドプロセスが完了すると削除されています。

packer_compute_engine
ビルドプロセス中に packer によって一時的に作成されたインスタンス

カスタムイメージは Google Cloud コンソールの [Compute Engine]->[イメージ] から確認できます。

イメージ – Compute Engine – murata kazuhiro – Google Cloud コンソール
Packer によってビルドされたイメージ

動作確認

カスタムイメージを利用して Compute Engine VMインスタンスを起動してみます。Cloud Shell から以下コマンドを実行します。

gcloud compute instances create packer-test \
  --zone=asia-northeast1-a \
  --image=packer-test-image \
  --image-project=<Project Name>

インスタンスが起動したら SSH でログインし、Packer テンプレートで設定したスクリプト(NGINXのインストールと起動)の動作状況を確認します。

$ systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Fri 2024-08-30 07:39:50 UTC; 4min 12s ago

Packer のビルドプロセスでスクリプトが実行された状態でイメージが作成されたことが確認できました。

補足: 認証について

開発環境から Packer で Google Cloud サービスにアクセスするためには、User Application Default Credentials, JSONサービスアカウントキー、アクセストークンのいずれかが必要となります。また、実行するユーザやサービスアカウントに Compute インスタンス管理者 (v1)roles/compute.instanceAdmin.v1とサービス アカウント ユーザーroles/iam.serviceAccountUser のロールが必要です。

今回は Cloud Shell から オーナー権限を持つユーザで検証しており上記の考慮は不要でした。
詳細は以下をご参照ください。
https://developer.hashicorp.com/packer/integrations/hashicorp/googlecompute#authentication

おわりに

様々な制限によりコンテナを利用せずにVMインスタンス上にワークロードをデプロイするケースは多いかと思います。このような状況において Packer によるカスタムイメージの作成は、柔軟で効率の良いインフラ構築を実現できそうです。Google Cloud との親和性も高く、Compute Engine を利用する環境で迅速な開発サイクルの実現に寄与できるかと思います。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.